package TESTS;
import java.util.*;

/*
 * 1. This class will have just one object among all the bit torrent threads
 * 2. PeerID read in the contructor
 */

public class PeerData {

	/*
	 * - globalBit Vectors : [peer id][piece count] - Hash Maps is needed to
	 * convert PeerId to peerNumber - PeerNum varies from 0 to n-1
	 */
	private int peerID;
	private int numOfPeers;
	private int totalNumOfPieces;
	private boolean[][] globalFilePieceVector;
	private HashMap<Integer, Integer> peerIDHash = new HashMap<Integer, Integer>();
	private HashMap<Integer, Integer> peerNumHash = new HashMap<Integer, Integer>();

	private int numberOfPreferredNeighbors;
	private int unchokingInterval;
	private int optimisticUnchokingInterval;
	private long fileSize;
	private boolean interestedReceived[];
	private boolean interestedSend[];
	private int downloadSpeed[];
	private boolean hasCompleteFile;
	private Random randomGenerator;	

	private boolean shutdown = false;

	/*
	 * To update the choking or enter data into queues interested peers que and
	 * peer whom this peer has sent the ineterested Message
	 */

	/*
	 * The signature has been changed
	 */
	PeerData() {
		numOfPeers = 5;
		interestedReceived = new boolean[5];
		downloadSpeed = new int[5];
		randomGenerator = new Random();
	}
	
	void testConfig1(){
		for(int i=0; i<numOfPeers; i++)
			interestedReceived[i] = true;
		

		for(int i=2; i<numOfPeers; i++)
			downloadSpeed[i] = 0;
	}
	
	void testConfig2(){
		for(int i=2; i<numOfPeers; i++)
			interestedReceived[i] = true;
		
		for(int i=2; i<numOfPeers; i++)
			downloadSpeed[i] = i;
		
	}
	
	void testConfig4(){
		interestedReceived[0] = true;
		interestedReceived[1] = false;
		interestedReceived[2] = true;
		interestedReceived[3] = true;
		interestedReceived[4] = false;
		
		downloadSpeed[0] = 5;
		downloadSpeed[1] = 15;
		downloadSpeed[2] = 30;
		downloadSpeed[3] = 25;
		downloadSpeed[4] = 50;
	}
	
	void testOptUnchoke1(){
		boolean unchoked[] = {true, false, true, false, false};
		interestedReceived[0] = true;
		interestedReceived[1] = false;
		interestedReceived[2] = true;
		interestedReceived[3] = false;
		interestedReceived[4] = false;
		
		int nextOUN = nextOptUnchokedNeighbor(unchoked);
		if(nextOUN == -1)
			System.out.println("Test zero size for optUnchoke passed");
		else
			System.out.println("Test zero size for optUnchoke Failed");
		
		interestedReceived[3] = true;
		nextOUN = nextOptUnchokedNeighbor(unchoked);
		if(nextOUN == 3)
			System.out.println("Test 1a for optUnchoke passed");
		else
			System.out.println("Test 1a for optUnchoke Failed");
		
		interestedReceived[4] = true;
		nextOUN = nextOptUnchokedNeighbor(unchoked);
		if(nextOUN == 3 || nextOUN==4)
			System.out.println("Test 1b for optUnchoke passed");
		else
			System.out.println("Test 1b for optUnchoke Failed");
		
	}
	
	 // Functions for Preferred Neighbor selection
        
        // returns an array with peerNumber of k neighbors who provided highest download speed in last unchoking interval and are interested
        // return array value: -1 for no neighbor (<k interested)
	   public synchronized int[] topKNeighbors(int k){
           int[] topKNeighbors = new int[k];
           int index = 0;
           int[] sortedDownloadSpeed = createAndSort(downloadSpeed);
           
           // Get the top K neighbors interested
           for(int peerNum=0; peerNum<numOfPeers && index<k; peerNum++){
                   if(interestedReceived[peerNum] && containsInFirstK(sortedDownloadSpeed, downloadSpeed[peerNum], k)){
                           topKNeighbors[index++] = peerNum;
                   }
           }
           // Set the remaining places to -1: indicates <k neighbors interested
           if(index!=k){
                   while(index<k)
                           topKNeighbors[index++] = -1;
           }
           
           return topKNeighbors;
   }
    
        // helper function
        public boolean containsInFirstK(int array[], int checkMe, int k){
                for(int i=0; i<k; i++){
                        if(array[i] == checkMe)
                                return true;
                }
                
                return false;
        }
        
        // helper function - creates new array from input array, sorts in descending order and returns new array
        // TODO: check for concurrency on downloadSpeed[]
        private synchronized int[] createAndSort(int[] sortMe){
                int[] newArray = new int[sortMe.length];
                int index = 0;
                for(int i=0; i<sortMe.length; i++){
                    if(interestedReceived[i])
                    	newArray[index++] = sortMe[i];
                }
                Arrays.sort(newArray);
                // reverse array to bring in descending order
                for (int i=0; i<newArray.length/2; i++){
                        int temp = newArray[newArray.length - 1 - i];
                        newArray[newArray.length - 1 - i] = newArray[i];
                        newArray[i] = temp;
                }
                
                return newArray;
        }
        
        // Reset download of all neighbors but input param to zero
        public synchronized void resetDownloadExcept(int exceptPeerNum){
                for(int i=0; i<downloadSpeed.length; i++){
                        if(i != exceptPeerNum)
                                downloadSpeed[i] = 0;
                }
        }
        
        // returns peerNum for next guy to be opt. unchoked: who is interested but not in currently unchokedNeighbors
        // returns -1 if no neighbor to unchoke
        public synchronized int nextOptUnchokedNeighbor(boolean[] unchokedNeighbors){
                int numOfNotUnchoked = 0;
        		Vector<Integer> notUnchoked = new Vector<Integer>(numOfPeers);
                for(int peerNum=0; peerNum<numOfPeers; peerNum++){
                        if(interestedReceived[peerNum] && !unchokedNeighbors[peerNum]){
                                notUnchoked.add(peerNum);
                        }                               
                }
                numOfNotUnchoked = notUnchoked.size();
                if(numOfNotUnchoked == 0){
                	return -1;
                } else {
	                int random = randomGenerator.nextInt(notUnchoked.size());
	                return (Integer) notUnchoked.get(random);
                }
        }
}